﻿////////////////////////////////////////////////////////////////////////////////////////////
// File: ExplosionEffect.cs                                                               //
// Author: Mike Binnix                                                                    //
// Desc: Particle effect of an explosion.  Hanldes the creation of particles with the     //
//       correct attributes and adds them into the ParticleManager.                       //
////////////////////////////////////////////////////////////////////////////////////////////

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace SlothEngine.Physics.ParticleEffects
{
    public class ExplosionEffect
    {
        #region Attributes
        protected Vector2 position;
        protected Texture2D smokeImage;
        protected Texture2D explosionImage;
        protected float smokeMinVelocity;
        protected float smokeMaxVelocity;
        protected float explosionMinVelocity;
        protected float explosionMaxVelocity;
        protected TimeSpan smokeParticleAge;
        protected TimeSpan explosionParticleAge;
        protected float smokeScale;
        protected float explosionScale;
        protected int maxSmokeParticles;
        protected int maxExplosionParticles;
        protected List<EffectParticle> smokeList;
        protected List<EffectParticle> explosionList;
        protected Random rand;
        protected bool active;
        #endregion

        #region Properties
        /// <summary>
        /// Gets or sets the current position.
        /// </summary>
        public Vector2 Position
        {
            get
            {
                return position;
            }
            set
            {
                position = value;
            }
        }
        /// <summary>
        /// Gets or sets the image for background particles.
        /// </summary>
        public Texture2D BackgroundImage
        {
            get
            {
                return smokeImage;
            }
            set
            {
                smokeImage = value;
            }
        }
        /// <summary>
        /// Gets or sets the image for foreground particles.
        /// </summary>
        public Texture2D ForegroundImage
        {
            get
            {
                return explosionImage;
            }
            set
            {
                explosionImage = value;
            }
        }
        /// <summary>
        /// Gets or sets the min velocity for background particles.
        /// </summary>
        public float BackgroundMinVelocity
        {
            get
            {
                return smokeMinVelocity;
            }
            set
            {
                smokeMinVelocity = value;
            }
        }
        /// <summary>
        /// Gets or sets the max velocity for background particles.
        /// </summary>
        public float BackgroundMaxVelocity
        {
            get
            {
                return smokeMaxVelocity;
            }
            set
            {
                smokeMaxVelocity = value;
            }
        }
        /// <summary>
        /// Gets or sets the min velocity for foreground particles.
        /// </summary>
        public float ForegroundMinVelocity
        {
            get
            {
                return explosionMinVelocity;
            }
            set
            {
                explosionMinVelocity = value;
            }
        }
        /// <summary>
        /// Gets or sets the max velocity for foreground particles.
        /// </summary>
        public float ForegroundMaxVelocity
        {
            get
            {
                return explosionMaxVelocity;
            }
            set
            {
                explosionMaxVelocity = value;
            }
        }
        /// <summary>
        /// Gets or sets the max age of a background particle.
        /// </summary>
        public TimeSpan BackgroundParticleMaxAge
        {
            get
            {
                return smokeParticleAge;
            }
            set
            {
                smokeParticleAge = value;
            }
        }
        /// <summary>
        /// Gets or sets the max age of a foreground particle.
        /// </summary>
        public TimeSpan ForegroundParticleMaxAge
        {
            get
            {
                return explosionParticleAge;
            }
            set
            {
                explosionParticleAge = value;
            }
        }
        /// <summary>
        /// Gets or sets the scale for the background particle.
        /// </summary>
        public float BackgroundScale
        {
            get
            {
                return smokeScale;
            }
            set
            {
                if (value < 0.0f)
                    smokeScale = 0.0f;
                else
                    smokeScale = value;
            }
        }
        /// <summary>
        /// Gets or sets the scale for the foreground particle.
        /// </summary>
        public float ForegroundScale
        {
            get
            {
                return explosionScale;
            }
            set
            {
                if (value < 0.0f)
                    explosionScale = 0.0f;
                else
                    explosionScale = value;
            }
        }
        /// <summary>
        /// Gets of sets the maximum number of particles for the background particles.
        /// </summary>
        public int BackgroundMaxParticles
        {
            get
            {
                return maxSmokeParticles;
            }
            set
            {
                if (value < 0)
                    maxSmokeParticles = 0;
                else
                    maxSmokeParticles = value;
            }
        }
        /// <summary>
        /// Gets or ses the maximum number of particles for the foreground particles.
        /// </summary>
        public int ForegroundMaxParticles
        {
            get
            {
                return maxExplosionParticles;
            }
            set
            {
                if (value < 0)
                    maxExplosionParticles = 0;
                else
                    maxExplosionParticles = value;
            }
        }
        #endregion

        #region Constructors
        public ExplosionEffect()
        {
            position = Vector2.Zero;
            smokeImage = null;
            explosionImage = null;
            smokeMinVelocity = 0.0f;
            smokeMaxVelocity = 0.0f;
            explosionMinVelocity = 0.0f;
            explosionMaxVelocity = 0.0f;
            smokeParticleAge = TimeSpan.Zero;
            explosionParticleAge = TimeSpan.Zero;
            smokeScale = 1.0f;
            explosionScale = 1.0f;
            maxSmokeParticles = 10;
            maxExplosionParticles = 10;
            smokeList = new List<EffectParticle>();
            explosionList = new List<EffectParticle>();
            rand = new Random();
            active = true;
        }
        public ExplosionEffect(Vector2 position, Texture2D smokeImage, TimeSpan smokeMaxAge, Texture2D explosionImage, TimeSpan explosionMaxAge)
        {
            this.position = position;
            maxSmokeParticles = 10;
            maxExplosionParticles = 10;
            this.smokeImage = smokeImage;
            smokeParticleAge = smokeMaxAge;
            this.explosionImage = explosionImage;
            explosionParticleAge = explosionMaxAge;
            smokeMinVelocity = 0.0f;
            smokeMaxVelocity = 0.0f;
            explosionMinVelocity = 0.0f;
            explosionMaxVelocity = 0.0f;
            smokeScale = 1.0f;
            explosionScale = 1.0f;
            smokeList = new List<EffectParticle>();
            explosionList = new List<EffectParticle>();
            rand = new Random();
            active = true;
        }
        #endregion

        #region Methods
        /// <summary>
        /// Creates a new background and foreground particle and adds them to the effect.
        /// </summary>
        public virtual void CreateParticle()
        {
            // Create smoke particle if space available
            if (smokeList.Count < maxSmokeParticles)
            {
                EffectParticle smokeParticle = new EffectParticle();

                Vector2 tmpPosition;
                int tmp = rand.Next(-10, 10);
                tmpPosition.X = position.X + tmp;

                tmp = rand.Next(-10, 10);
                tmpPosition.Y = position.Y + tmp;

                smokeParticle.Position= tmpPosition;
                smokeParticle.Image = smokeImage;
                smokeParticle.MaxAge = smokeParticleAge;
                smokeParticle.Scale = smokeScale;
                smokeParticle.Rotation = 0.0f;
                smokeParticle.Alpha = 10.0f;
                smokeParticle.BeginFadeIn = TimeSpan.Zero;
                smokeParticle.BeginFadeOut = new TimeSpan(0, 0, 1);
                smokeParticle.FadeInDuration = new TimeSpan(0, 0, 1);
                smokeParticle.FadeOutDuration = new TimeSpan(0, 0, 1);
                smokeParticle.Origin = new Vector2(smokeImage.Width / 2, smokeImage.Height / 2);

                smokeParticle.Mass = 1.0f;
                smokeParticle.Damping = 0.50f;  // Add a lot of drag so smoke effect slowly slows down                      
                smokeParticle.Acceleration = new Vector2(0, 0); // No acceleration so smoke continues at its initial velocity

                // Find direction particle should move in
                double radians = MathHelper.ToRadians(rand.Next(0, 360));
                float xDir = (float)Math.Sin(radians) * rand.Next((int)smokeMinVelocity, (int)smokeMaxVelocity);

                radians = MathHelper.ToRadians(rand.Next(0, 360));
                float yDir = (float)Math.Cos(radians) * rand.Next((int)explosionMinVelocity, (int)explosionMaxVelocity);

                smokeParticle.Velocity = new Vector2(xDir, yDir);
                
                smokeList.Add(smokeParticle);
            }

            // Create explosion particle if space available
            if (explosionList.Count < maxExplosionParticles)
            {
                EffectParticle explosionParticle = new EffectParticle();

                Vector2 exPosition;
                int tmp = rand.Next(-5, 5);
                exPosition.X = position.X + tmp;

                tmp = rand.Next(-5, 5);
                exPosition.Y = position.Y + tmp;

                explosionParticle.Position = exPosition;
                explosionParticle.Image = explosionImage;
                explosionParticle.MaxAge = explosionParticleAge;
                explosionParticle.Scale = explosionScale;
                explosionParticle.Rotation = 0.0f;
                explosionParticle.Alpha = 10.0f;
                explosionParticle.BeginFadeIn = TimeSpan.Zero;
                explosionParticle.BeginFadeOut = new TimeSpan(0, 0, 0, 0, 400);
                explosionParticle.FadeInDuration = new TimeSpan(0, 0, 0, 0, 400);
                explosionParticle.FadeOutDuration = new TimeSpan(0, 0, 0, 0, 400);
                explosionParticle.Origin = new Vector2(explosionImage.Width / 2, explosionImage.Height / 2);

                explosionParticle.Mass = 1.0f;
                explosionParticle.Damping = 0.90f;  // Let explosion part of effect move more freely                      
                explosionParticle.Acceleration = new Vector2(0, 0); // No acceleration so explosion continues at its initial velocity

                // Find direction particle should move in
                double radians = MathHelper.ToRadians(rand.Next(0, 360));
                float xDir = (float)Math.Sin(radians) * rand.Next((int)smokeMinVelocity, (int)smokeMaxVelocity);

                radians = MathHelper.ToRadians(rand.Next(0, 360));
                float yDir = (float)Math.Cos(radians) * rand.Next((int)explosionMinVelocity, (int)explosionMaxVelocity);

                explosionParticle.Velocity = new Vector2(xDir, yDir);

                // Find direction particle should move in
                explosionList.Add(explosionParticle);
            }
        }
        /// <summary>
        /// Creatues particles as needed and updates all existing particles.
        /// </summary>
        /// <param name="gameTime">Time since last update.</param>
        public virtual void Update(GameTime gameTime)
        {
            // Remove old particles
            for (int i = 0; i < smokeList.Count; i++)
                if (smokeList[i].Destroy)
                    smokeList.RemoveAt(i);

            for (int i = 0; i < explosionList.Count; i++)
                if (explosionList[i].Destroy)
                    explosionList.RemoveAt(i);

            // Update all other particles
            foreach (EffectParticle particle in smokeList)
            {
                particle.Rotation = particle.Rotation + 0.005f;

                particle.Update(gameTime);
            }

            foreach (EffectParticle particle in explosionList)
            {
                particle.Rotation = particle.Rotation + 0.005f;

                particle.Update(gameTime);
            }

            // Check if all particles have been created
            if (smokeList.Count >= maxSmokeParticles && explosionList.Count >= maxExplosionParticles)
                active = false;

            // Create new particles if there is still room
            if (active)
                CreateParticle();
        }
        /// <summary>
        /// Draws the particles to the screen.
        /// </summary>
        /// <param name="spriteBatch">SpriteBatch used for drawing.</param>
        public virtual void Draw(SpriteBatch spriteBatch)
        {
            foreach (EffectParticle particle in smokeList)
                particle.Draw(spriteBatch);

            foreach (EffectParticle particle in explosionList)
                particle.Draw(spriteBatch);
        }
        #endregion
    }
}
